﻿using Bouyei.GeoCore.Converters;
using Bouyei.GeoCore.Geometries;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Bouyei.GeoCore.GeoParsers.Tiff
{
    public class TiffReader:BaseFile
    {
        private BitExtensions bitExt = null;

        public TiffReader(string tiffFile)
            :base(tiffFile)
        {

        }

        public TiffInfo Reader()
        {
            var TiffInfo = new TiffInfo()
            {
                FHeader = new IFH(),
                FDirectory = new List<IFD>(2)
            };
            var reader = BinReaderStream();

            using (reader)
            {
                TiffInfo.FHeader.OrderFlag = reader.ReadUInt16();
                ByteOrder byteOrder = TiffInfo.FHeader.OrderFlag == 0x4949 ? ByteOrder.LittleEndian : ByteOrder.BigEndian;
                bitExt = new BitExtensions(byteOrder);
                //header
                TiffInfo.FHeader.TiffFlag = (UInt16)bitExt.ToInt16(reader);
                TiffInfo.FHeader.IFDOffset = (UInt32)bitExt.ToInt32(reader);
                uint offset = TiffInfo.FHeader.IFDOffset;

                again:
                IFD iFD = new IFD();

                if (offset > 0)
                {
                    //定位偏移地址
                    reader.BaseStream.Seek(offset, SeekOrigin.Begin);
                }

                //directory
                iFD.DECount = (UInt16)bitExt.ToInt16(reader);

                //IFD
                iFD.FDAttribute = DecodeIFD(iFD, reader);

                iFD.NextOffset = (UInt32)bitExt.ToInt32(reader);

                TiffInfo.FDirectory.Add(iFD);

                if (iFD.NextOffset > 0)
                {
                    offset = iFD.NextOffset;
                    goto again;
                }
            }
            return TiffInfo;
        }

        private FDAttribute DecodeIFD(IFD iFd, BinaryReader reader)
        {
            //DE
            iFd.DE = new DE[iFd.DECount];
            FDAttribute attr = new FDAttribute();

            for (int i = 0; i < iFd.DECount; ++i)
            {
                var de = new DE()
                {
                    Tag = bitExt.ToInt16(reader),
                    DType = (DataType)bitExt.ToInt16(reader),
                    Count = bitExt.ToInt32(reader),
                    ValueOffset = bitExt.ToInt32(reader)
                };

                switch (de.Tag)
                {
                    case 0x0100:
                        attr.With = de.ValueOffset;
                        break;
                    case 0x0101:
                        attr.Height = de.ValueOffset;
                        break;
                    case 0x0102:
                        attr.Depth = de.ValueOffset;
                        break;
                    case 0x0103:
                        attr.IsCompression = de.ValueOffset == 5 ? true : false;
                        break;
                    case 0x0106:
                        attr.IsAntiColor = de.ValueOffset == 1 ? true : false;
                        break;
                    case 0x0111:
                        attr.ScanOffset = de.ValueOffset;
                        break;
                    case 0x0116:
                        attr.ScanCount = de.ValueOffset;
                        break;
                    case 0x0117:
                        attr.TotalLength = de.ValueOffset;
                        break;
                    case 0x011A:
                        {
                            reader.BaseStream.Seek(de.ValueOffset, SeekOrigin.Begin);
                            float s = bitExt.ToInt16(reader) * 1f;
                            uint b = (UInt32)bitExt.ToInt32(reader);
                            attr.HResolution = b > 0 ? (s / b) : 0;
                        }
                        break;
                    case 0x011B:
                        {
                            reader.BaseStream.Seek(de.ValueOffset, SeekOrigin.Begin);
                            float s = bitExt.ToInt16(reader) * 1f;
                            uint b = (uint)bitExt.ToInt32(reader);
                            attr.VResolution = b > 0 ? s / b : 0;
                        }
                        break;
                    case 0x0128:
                        attr.ResolutionUnit = (ushort)de.ValueOffset;
                        break;
                    case 0x0131:
                        {
                            byte[] t = reader.ReadBytes(de.Count);
                            if (bitExt.ByteOrder == ByteOrder.BigEndian) t = t.Reverse().ToArray();

                            attr.Source = Encoding.Default.GetString(t);
                        }
                        break;
                    case 0x0132:
                        {
                            byte[] t = reader.ReadBytes(de.Count);
                            if (bitExt.ByteOrder == ByteOrder.BigEndian) t = t.Reverse().ToArray();

                            attr.Date = Encoding.Default.GetString(t);
                        }
                        break;
                    case 0x0140:
                        {
                            attr.PlatteOffset = de.ValueOffset;
                        }
                        break;
                }

                iFd.DE[i] = de;
            }
            return attr;
        }

        public override void Dispose()
        {
            base.Dispose();
        }
    }
}
